#!/usr/bin/env python

"""
    ledd
    Front-panel LED control daemon for SONiC
"""

import getopt
import sys

from sonic_py_common import daemon_base
from sonic_py_common import multi_asic
from sonic_py_common.interface import backplane_prefix, inband_prefix, recirc_prefix
from swsscommon import swsscommon

#============================= Constants =============================

VERSION = '1.0'

SYSLOG_IDENTIFIER = "ledd"

USAGE_HELP = """
Usage: ledd [options]

Options:
  -h,--help       Print this usage statement and exit
  -v,--version    Print version information and exit
"""

LED_MODULE_NAME = "led_control"
LED_CLASS_NAME = "LedControl"

SELECT_TIMEOUT = 1000

LEDUTIL_LOAD_ERROR = 1


class DaemonLedd(daemon_base.DaemonBase):

    def __init__(self):
        daemon_base.DaemonBase.__init__(self, SYSLOG_IDENTIFIER)

        # Load platform-specific LedControl module
        try:
            self.led_control = self.load_platform_util(LED_MODULE_NAME, LED_CLASS_NAME)
        except Exception as e:
            self.log_error("Failed to load ledutil: %s" % (str(e)), True)
            sys.exit(LEDUTIL_LOAD_ERROR)

        if multi_asic.is_multi_asic():
            # Load the namespace details first from the database_global.json file.
            swsscommon.SonicDBConfig.initializeGlobalConfig()

        # Get the namespaces in the platform. For multi-asic devices we get the namespaces
        # of front-end ascis which have front-panel interfaces.
        namespaces = multi_asic.get_front_end_namespaces()

        # Subscribe to PORT table notifications in the Application DB
        appl_db = {}
        self.sst = {}
        self.sel = swsscommon.Select()

        for namespace in namespaces:
            # Open a handle to the Application database, in all namespaces
            appl_db[namespace] = daemon_base.db_connect("APPL_DB", namespace=namespace)
            self.sst[namespace] = swsscommon.SubscriberStateTable(appl_db[namespace], swsscommon.APP_PORT_TABLE_NAME)
            self.sel.addSelectable(self.sst[namespace])

    # Run daemon
    def run(self):
        # Use timeout to prevent ignoring the signals we want to handle
        # in signal_handler() (e.g. SIGTERM for graceful shutdown)
        (state, selectableObj) = self.sel.select(SELECT_TIMEOUT)

        if state == swsscommon.Select.TIMEOUT:
            # Do not flood log when select times out
            return 1

        if state != swsscommon.Select.OBJECT:
            self.log_warning("sel.select() did not return swsscommon.Select.OBJECT")
            return 2

        # Get the redisselect object from selectable object
        redisSelectObj = swsscommon.CastSelectableToRedisSelectObj(selectableObj)

        # Get the corresponding namespace from redisselect db connector object
        namespace = redisSelectObj.getDbConnector().getNamespace()

        (key, op, fvp) = self.sst[namespace].pop()
        if fvp:
            # TODO: Once these flag entries have been removed from the DB,
            # we can remove this check
            if key in ["PortConfigDone", "PortInitDone"]:
                return 3

            fvp_dict = dict(fvp)

            if op == "SET" and "oper_status" in fvp_dict:
                if not key.startswith((backplane_prefix(), inband_prefix(), recirc_prefix())):
                    self.led_control.port_link_state_change(key, fvp_dict["oper_status"])
        else:
            return 4

        return 0


def main():
    # Parse options if provided
    if len(sys.argv) > 1:
        try:
            (options, remainder) = getopt.getopt(sys.argv[1:], 'hv', ['help', 'version'])
        except getopt.GetoptError as e:
            print(e)
            print(USAGE_HELP)
            sys.exit(1)

        for opt, arg in options:
            if opt == '--help' or opt == '-h':
                print(USAGE_HELP)
                sys.exit(0)
            elif opt == '--version' or opt == '-v':
                print('ledd version {}'.format(VERSION))
                sys.exit(0)

    ledd = DaemonLedd()

    # Listen indefinitely for changes to the PORT table in the Application DB's
    while True:
        ledd.run()


if __name__ == '__main__':
    main()
